package com.zrrd.straw1.controller;

import com.zrrd.straw1.service.IUserService;
import com.zrrd.straw1.service.impl.ServiceException;
import com.zrrd.straw1.vo.RegisterVo;
import com.zrrd.straw1.vo.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.websocket.Session;
import java.io.File;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.UUID;

@Slf4j
@RestController
public class SystemController {

    //根据登录身份跳转到不同的问题详情页








    //为了实现根据用户角色跳转不同页面，首先使用Security提供的类型 GrantedAuthority
    //GrantedAuthority 是 security 提供的用来保存用户权限，角色的类型。
    //虽然是字符串比较，但要交给 安全框架内部完成。
    public static final GrantedAuthority STUDENT = new SimpleGrantedAuthority("ROLE_STUDENT");
    public static final GrantedAuthority TEACHER = new SimpleGrantedAuthority("ROLE_TEACHER");
    //Authentication ：认证， 识别当前用户是不是系统内部人员。
    //Authority      : 授权， 是系统内部人员，但有没有权限访问相应的资源。


    @GetMapping("/question/detail.html ")
    public ModelAndView detail(@AuthenticationPrincipal UserDetails userDetails){
        //是老师则跳到老师首页，注意一个用户可能会有老师，学生多重身份，就该先判断是不是老师。
        if( userDetails.getAuthorities().contains(TEACHER)){
            return new ModelAndView("question/detail_teacher");
        }if(userDetails.getAuthorities().contains(STUDENT)){
            return new ModelAndView("question/detail");
        }
        //没有权限 不返回页面
        return null;
    }

  /*  @GetMapping("/question/detail.html")
    public ModelAndView detail(){
        //Springboot 的视图解析器前缀是 "resources/templates/"
        // 所以文件名不加 / 开头。
        return new ModelAndView("question/detail");
    }
*/


    @GetMapping("/index.html")  //index.html 是在 SecurityConfig 中配置的登录成功自动跳转的目标路径
    public ModelAndView method1( @AuthenticationPrincipal UserDetails userDetails ){
        //是老师则跳到老师首页，注意一个用户可能会有老师，学生多重身份，就该先判断是不是老师。
        if( userDetails.getAuthorities().contains(TEACHER)){
            return new ModelAndView("index_teacher");
        }else if( userDetails.getAuthorities().contains(STUDENT)) {
            //用来指定将数据绑定到哪个模板上！
            return new ModelAndView("index");
        }
        //如果都不是，没有身份
        return null ;
    }

    //所有的请求都被 DispatcherServlet 拦截了，如果有服务请求映射，返回控制器
    //如果没有，才去找静态资源。如果静态资源没有，返回 404 ！
    //控制器的服务请求和 static 静态资源文件路径名冲突，控制器优先 ！
    //注掉：这种方法，凡是通过认证的用户都可以直接访问。把这个入口关闭掉。
    //@GetMapping("/index_teacher.html")
    //public ModelAndView indexTeacher(){
        //return new ModelAndView("index_teacher");
    //}


    //spring 提供的注解，获取application.yml中的参数
    //Spring容器创建下面的 File 实例和String 实例。
    @Value("${straw.resources.path}")  //file:D:/upload 值
    private File resourcePath;
    @Value("${straw.resources.host}")  //localhost:8889 值
    private String resourceHost;

    //如何解决文件重名的问题？
    //1.自己加一个唯一名重新命名：pic1.jpg,  pic2.jpg, pic3.jpg  多线程下会存在重名
    //2.Java提供 UUID 类，提供全局唯一的字符串
    //3.按照年月日来创建文件目录。方便维护管理，也减少重名的概率
    @PostMapping("/upload/file")
    public Result uploadFile( MultipartFile uploadFile ) throws IOException {

        //1.管理保存路径
        //按年月日创建目录: 静态方法，将日期转换成指定格式的字符串，2024/04/27
        String path = DateTimeFormatter.ofPattern("yyyy/MM/dd").format(LocalDateTime.now());
        File folder = new File(resourcePath, path );  //抽像路径：D:/upload/2024/04/27
        folder.mkdirs();  //必须使用带S的方法！

        //2.管理文件名
        //获取原始文件名
        String originalFileName = uploadFile.getOriginalFilename();   //20240319.jpg.txt
        //截取后缀名
        String ext = originalFileName.substring(originalFileName.lastIndexOf('.'));
        //保存服务器上的文件名
        String fileName = UUID.randomUUID().toString() + ext ;
        //服务器磁盘保存的文件路径名
        File file = new File( folder , fileName );
        System.out.println( "保存的文件路径名： " + file.getAbsolutePath() );

        //3.写到磁盘上
        uploadFile.transferTo( file );

        //4.返回给前端文件的访问地址: IP + Port + 路径
        //           localhost:8889       2024/04/27        uuid.jpg
        String url = resourceHost  + "/" + path     +  "/"  + fileName ;
        //前端拿到这个 url ，就可以绑定请求链接，访问这个文件
        System.out.println("url: " + url );
        return Result.ok( url );
    }



    @GetMapping("/question/create.html")
    public ModelAndView createhtml(){
        // 由视图解析器完成路径字符串拼接 “/templates/” + 文件路径名 + “.html"
        // 这里要注意，文件路径名必须和 templates/ 目录下路径名一致。
        return new ModelAndView("question/create");
    }



    @Autowired
    IUserService userService ;

    //返回注册页面的
    //1.因为类使用 @RestController,如果想返回视图，使用 ModelAndView
    @GetMapping("/register.html")
    public ModelAndView regStudent(){
        //创建 MAV 实例时，参数即是视图名。
        //return new ModelAndView("register");
        ModelAndView mav = new ModelAndView();
        mav.setViewName("register");
        return  mav ;
    }

    //提供注册服务（功能）
    //注解别选错了。只能处理 POST 请求方式。如果写错了，会返回 405，请求方式不匹配
    //此方法谁调用 ？ SpringMVC 框架调用。
    //参数谁来给？    SpringMVC 初始化，而且按名字来自动匹配。名字对不上，匹配不上。
    //如果你的参数超过3个，就要考虑设置一个类型来封装一下。《人月传说》
    //设置一个实体类。好处是代码简化。语义清晰，传递数据时会非常散乱，代码臃肿
    //需要专门设计一个类，来整理散乱的参数。这个类是专门和前端映射的。没有现成的实体类。
    @PostMapping("/register")
    public Result regStudent(@Validated RegisterVo registerVo , BindingResult result) {
        //先对参数做格式验证
        if( result.hasErrors()){
            //保存错误消息
            String error = result.getFieldError().getDefaultMessage();
            //直接返回
            return Result.unprocesableEntity( error );
        }
        if( !registerVo.getPassword().equals(registerVo.getConfirm())){
            return Result.unprocesableEntity("确认密码不一致");
        }




        //接收前端发过来的参数？
        //参数是由 SpringMVC创建和初始化的，在方法体中直接使用即可。
        //System.out.println( registerVo );
        try {
            userService.registerStudent(registerVo);
            return Result.created("注册完成！");
        }catch (ServiceException e){
            //查看效果，在“控制台” 上输出的信息。开发阶段可以，但生产环境下不存在控制台概念
            //生产环境下可以输出到日志文件当中。lombok 提供一个注解，快速使用日志功能。
            //在类名上加 @Slf4j 注解， 在程序代码中直接使用  log 对象。
            //System.out.println("注册失败");
            log.error("注册失败",e);
            return Result.failed( e );
        }
    }

    /*
        现在网页基本是以异步请求，局部刷新为主。
        加载，轮播图，图片
        AJAX : js -> 异步请求技术。
        XML 保存数据
        JSON 字符串：通过大括号 ，中括号，冒号，逗号 来表示数组层级及对象。
        局部刷新： 数据格式多种多样 -> 怎么样能统一地处理。
        返回的类型不一样，处理方式就不能一样
        user, class, userrole,  开发一个统一的处理过程 。
     */



}
